﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using md.imi.membrane.common.Interfaces;
using md.imi.membrane.constants;
using md.imi.membranes.gui.Utils;
using md.imi.membranes.objects;
using md.imi.membranes.objects.membranes;
using md.imi.membranes.objects.strings;

namespace md.imi.membranes.gui.elements
{
    public partial class MembraneG : Panel, IHandleMembraneSelection
    {
        #region Delegates

        public delegate void MmebraneSelectionHandler(MembraneG sender, Membrane membrane);

        #endregion Delegates

        #region Events

        public event MmebraneSelectionHandler EventMembraneSelection;

        #endregion Events

        #region Private fields

        private Point _Offset = Point.Empty;

        //  Resize regions
        private Rectangle szLeft, szRight, szTop, szBottom;
        private readonly object redrawLockObject = new object();

        private enum ControlResizeType
        {
            ResizeNone,
            ResizeLeft,
            ResizeRight,
            ResizeTop,
            ResizeBottom
        }  ;

        private ControlResizeType resizeType = ControlResizeType.ResizeNone;

        #endregion Private fields

        #region Public fields

        public bool IsSelected = false;
        private Solution highSolutions = null;

        public Color MembraneParnthesisColor { get; set; }

        public Color MembraneChargeColor { get; set; }

        public Membrane Membrane { get; set; }

        #endregion Public fields

        #region Constructors

        /// <summary>
        ///     Default constructor
        /// </summary>
        public MembraneG()
        {
            MembraneParnthesisColor = Color.Red;
            MembraneChargeColor = Color.Blue;

            InitializeComponent();
        }

        /// <summary>
        ///     Membrane initialization constructor
        /// </summary>
        /// <param name="membrane"></param>
        public MembraneG(Membrane membrane)
            : this()
        {
            Membrane = membrane;
            Location = new Point(membrane.GuiInfo.Location.X,
                                 membrane.GuiInfo.Location.Y);
            Size = new Size(membrane.GuiInfo.Size.Width,
                            membrane.GuiInfo.Size.Height);
        }

        #endregion Constructors

        #region Drawing routines

        public void DrawRoundRect(Graphics g, Pen p, float x, float y, float width, float height, float radius)
        {
            var gp = new GraphicsPath();

            gp.AddLine(x + radius, y, x + width - (radius * 2), y); // Line
            gp.AddArc(x + width - (radius * 2), y, radius * 2, radius * 2, 270, 90); // Corner
            gp.AddLine(x + width, y + radius, x + width, y + height - (radius * 2)); // Line
            gp.AddArc(x + width - (radius * 2), y + height - (radius * 2), radius * 2, radius * 2, 0, 90); // Corner
            gp.AddLine(x + width - (radius * 2), y + height, x + radius, y + height); // Line
            gp.AddArc(x, y + height - (radius * 2), radius * 2, radius * 2, 90, 90); // Corner
            gp.AddLine(x, y + height - (radius * 2), x, y + radius); // Line
            gp.AddArc(x, y, radius * 2, radius * 2, 180, 90); // Corner
            gp.CloseFigure();

            g.DrawPath(p, gp);
            gp.Dispose();
        }

        /// <summary>
        ///     Custom paint method
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            //---------------------------------------------------------------//
            // membrane square paranthesis
            /*e.Graphics.DrawLine(new Pen(MembraneParnthesisColor), 10, 10, 10, Height - 10);
            e.Graphics.DrawLine(new Pen(MembraneParnthesisColor), Width - 10, 10, Width - 10, Height - 10);

            e.Graphics.DrawLine(new Pen(MembraneParnthesisColor), 10, 10, 15, 10);
            e.Graphics.DrawLine(new Pen(MembraneParnthesisColor), 10, Height - 10, 15, Height - 10);

            e.Graphics.DrawLine(new Pen(MembraneParnthesisColor), Width - 15, 10, Width - 10, 10);
            e.Graphics.DrawLine(new Pen(MembraneParnthesisColor), Width - 15, Height - 10, Width - 10, Height - 10);
            */

            DrawRoundRect(e.Graphics, new Pen(MembraneParnthesisColor), 10, 10, Width - 20, Height - 20, 16);
            //---------------------------------------------------------------//
            // draw charge color if not neutral
            if (Membrane != null && Membrane.Charge != Charge.Neutral)
            {
                e.Graphics.DrawLine(new Pen(MembraneChargeColor), Width - 2, 10, Width - 8, 10);
                if (Membrane.Charge == Charge.Positive)
                    e.Graphics.DrawLine(new Pen(MembraneChargeColor), Width - 5, 7, Width - 5, 14);
            }

            //---------------------------------------------------------------//
            // draw membrane label
            if (Membrane != null && !String.IsNullOrEmpty(Membrane.Label))
            {
                var fnt = new Font(FontFamily.GenericMonospace, 7, FontStyle.Regular);
                e.Graphics.DrawString(Membrane.Label, fnt, new SolidBrush(Color.Black),
                    Width - Membrane.Label.Length * fnt.SizeInPoints, Height - 5 - fnt.SizeInPoints);
            }

            //---------------------------------------------------------------//
            // draw membrane content
            // Membrane content is drawn inside the membrane, on the upper part. In this way, the other
            // membranes can be drawn inside the current membrane
            if (Membrane != null && Membrane.Multiset != null)
            {
                DrawMembraneMultiset(e.Graphics, Membrane.Multiset);
            }

            //---------------------------------------------------------------//
            // draw selection frame
            //---------------------------------------------------------------//
            if (IsSelected)
            {
                // draw dashed border
                // draw margine patrulateress
                float[] dashValues = { 5, 2, 5, 2 };
                var pen = new Pen(Color.Blue) { DashPattern = dashValues };

                e.Graphics.DrawLine(pen, 2, 2, 2, Height - 2);
                e.Graphics.DrawLine(pen, Width - 2, 2, Width - 2, Height - 2);

                e.Graphics.DrawLine(pen, 2, 2, Width - 2, 2);
                e.Graphics.DrawLine(pen, 2, Height - 2, Width - 2, Height - 2);
            }

            //---------------------------------------------------------------//
            // draw resize points
            //---------------------------------------------------------------//
            if (IsSelected)
            {
                lock (redrawLockObject)
                {
                    var pen = new Pen(Color.Black); //  { DashPattern = dashValues };

                    szLeft = new Rectangle(0, (Height / 2) - 2, 4, 4);
                    e.Graphics.DrawRectangle(pen, szLeft); // left

                    szRight = new Rectangle(Width - 4, (Height / 2) - 2, 4, 4);
                    e.Graphics.DrawRectangle(pen, szRight); // right

                    szTop = new Rectangle(Width / 2 - 2, 0, 4, 4);
                    e.Graphics.DrawRectangle(pen, szTop); // top

                    szBottom = new Rectangle(Width / 2 - 2, Height - 4, 4, 4);
                    e.Graphics.DrawRectangle(pen, szBottom); // bottom
                }
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="g"></param>
        /// <param name="multiset"></param>
        private void DrawMembraneMultiset(Graphics g, List<StringObject> multiset)
        {
            if (highSolutions == null)
            {
                Console.WriteLine("draw unselected for " + this.Membrane.Label);
                String content = "";
                foreach (var sObj in multiset)
                {
                    if (content.Length > 0)
                    {
                        content += ", ";
                    }

                    content += sObj.ToString();
                }

                using (var fnt = new Font("Arial", 9, FontStyle.Regular, GraphicsUnit.Point))
                {
                    var rectF1 = new RectangleF(12, 12, Width - 12, Height - 12);
                    g.DrawString(content, fnt, Brushes.Black, rectF1);
                }
            }
            else
            {
                Console.WriteLine("draw selected for " + this.Membrane.Label);

                var rectF1 = new Rectangle(12, 12, Width - 12, Height - 12);

                string content =
                    @"{\rtf1\ansi\ansicpg1251\deff0\deflang1049{\fonttbl{\f0\fnil\fcharset0 Calibri;}}
{\colortbl ;\red255\green0\blue0;}
{\*\generator Msftedit 5.41.21.2510;}\viewkind4\uc1\pard\sa200\sl276\slmult1\lang9\f0\fs22 ";

                for (int i = 0; i < multiset.Count; i++)
                {
                    bool isFirstAlphabetObject = true;
                    content += "<";

                    if (i == highSolutions.ProcessedStringIndex)
                    {
                        for (int j = 0; j < multiset[i].Count; j++)
                        {
                            if (!isFirstAlphabetObject)
                            {
                                content += ".";
                            }
                            else
                            {
                                isFirstAlphabetObject = false;
                            }

                            if (j == highSolutions.ProcessedSubStringIndex)
                            {
                                content += String.Format(@"\cf1\b {0}\cf0\b0 ", multiset[i][j]);
                            }
                            else
                            {
                                content += String.Format("{0}", multiset[i][j]);
                            }
                        }
                    }
                    else
                    {
                        content += multiset[i].ToString().Replace("<", "").Replace(">", "");
                        // draw normal
                    }

                    content += ">";
                }

                content += @"\par
}
 ";

                g.DrawRtfText(content, rectF1);
            }
        }

        #endregion Drawing routines

        #region Control movements

        /// <summary>
        ///     From rectangle selection, determine resize type
        /// </summary>
        /// <param name="pt"></param>
        /// <returns></returns>
        private ControlResizeType GetResizeType(Point pt)
        {
            if (szLeft.Contains(pt))
            {
                return ControlResizeType.ResizeLeft;
            }

            if (szRight.Contains(pt))
            {
                return ControlResizeType.ResizeRight;
            }

            if (szTop.Contains(pt))
            {
                return ControlResizeType.ResizeTop;
            }
            else if (szBottom.Contains(pt))
            {
                return ControlResizeType.ResizeBottom;
            }

            return ControlResizeType.ResizeNone;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MembraneG_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (EventMembraneSelection != null)
                {
                    EventMembraneSelection(this, Membrane);
                }

                lock (redrawLockObject)
                {
                    _Offset = new Point(e.X, e.Y);
                    resizeType = GetResizeType(_Offset);
                }
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MembraneG_MouseMove(object sender, MouseEventArgs e)
        {
            ControlResizeType ctrlResizeType = GetResizeType(new Point(e.X, e.Y));

            if (ctrlResizeType == ControlResizeType.ResizeLeft ||
                ctrlResizeType == ControlResizeType.ResizeRight)
            {
                Cursor = Cursors.SizeWE;
            }
            else if (ctrlResizeType == ControlResizeType.ResizeBottom ||
                        ctrlResizeType == ControlResizeType.ResizeTop)
            {
                Cursor = Cursors.SizeNS;
            }
            else
            {
                Cursor = Cursors.Arrow;
            }

            // perform movement
            if (_Offset != Point.Empty && resizeType == ControlResizeType.ResizeNone)
            {
                Point newlocation = this.Location;
                newlocation.X += e.X - _Offset.X;
                newlocation.Y += e.Y - _Offset.Y;
                this.Location = newlocation;
                this.Membrane.GuiInfo.Location = newlocation;
                Console.WriteLine(String.Format("Location [{0}] : {1}, {2}", this.Membrane.Label,
                    this.Membrane.GuiInfo.Location.X, this.Membrane.GuiInfo.Location.Y));
                return;
            }

            // perform resize
            if (resizeType == ControlResizeType.ResizeRight)
            {
                this.Width = e.X;
                this.Invalidate();
                this.Membrane.GuiInfo.Size = this.Size;
            }

            if (resizeType == ControlResizeType.ResizeBottom)
            {
                this.Height = e.Y;
                this.Invalidate();
                this.Membrane.GuiInfo.Size = this.Size;
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MembraneG_MouseUp(object sender, MouseEventArgs e)
        {
            _Offset = Point.Empty;
            resizeType = ControlResizeType.ResizeNone;
        }

        #endregion Control movements

        #region IHandleMembraneSelection implementation

        /// <summary>
        ///     Perform deselection of all submebranes contained as
        /// membranes controls
        /// </summary>
        public void DeselectAllSubmemranes()
        {
            foreach (var ctrl in Controls)
            {
                if (ctrl is MembraneG)
                {
                    (ctrl as MembraneG).IsSelected = false;
                    (ctrl as MembraneG).DeselectAllSubmemranes();
                    (ctrl as MembraneG).Invalidate();
                }
            }
        }

        /// <summary>
        ///     Current membrane has been selected. The other child membranes
        /// should be unselected.
        /// </summary>
        /// <param name="senderObj"></param>
        /// <param name="membraneObj"></param>
        public void MembraneSelectionEventHandler(object senderObj, object membraneObj)
        {
            var sender = senderObj as MembraneG;
            var memebrane = membraneObj as Membrane;

            if (sender == null || memebrane == null) return;

            foreach (var ctrl in Controls)
            {
                if (!(ctrl is MembraneG)) continue;

                var m = ctrl as MembraneG;

                if (m == sender)
                {
                    m.IsSelected = true;
                    m.DeselectAllSubmemranes();
                    m.BringToFront();
                }
                else
                    m.IsSelected = false;
            }

            if ((sender.Parent != null) && (sender.Parent is MembraneG))
            {
                (sender.Parent as MembraneG).IsSelected = false;
                (sender.Parent as MembraneG).Invalidate();
            }
        }

        #endregion IHandleMembraneSelection implementation

        #region Control key pressing handlers

        /// <summary>
        ///     Key press event handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void HandleKeyPress(Keys keys)
        {
            if (IsSelected)
            {
                Point newlocation = this.Location;

                switch (keys)
                {
                    case Keys.Up:
                        newlocation.Y -= 5;
                        break;

                    case Keys.Down:
                        newlocation.Y += 5;
                        break;

                    case Keys.Left:
                        newlocation.X -= 5;
                        break;
                    case Keys.Right:
                        newlocation.X += 5;
                        break;
                    default:
                        return;
                }

                this.Location = newlocation;
                Invalidate();
            }
            else
            {
                foreach (var ctrl in Controls)
                {
                    if (ctrl is MembraneG)
                    {
                        (ctrl as MembraneG).HandleKeyPress(keys);
                    }
                }
            }
        }

        #endregion Control key pressing handlers

        #region Highlightings 
        /// <summary>
        ///
        /// </summary>
        /// <param name="selectedSolution"></param>
        public void HighlightSolutions(List<Solution> selectedSolutions)
        {
            if (selectedSolutions != null && selectedSolutions.Count > 0)
            {
                // draw selected solution if any, and is for current membrane
                if (selectedSolutions[0] != null && selectedSolutions[0].Membrane != null &&
                    selectedSolutions[0].Membrane.Id == Membrane.Id)
                {
                    // draw
                    highSolutions = selectedSolutions[0];
                }
                else
                {
                    highSolutions = null;
                }
            }
            else
            {
                highSolutions = null;
            }

            Invalidate();

            // send info to other membranes
            foreach (var ctrl in Controls)
            {
                if (ctrl is MembraneG)
                {
                    (ctrl as MembraneG).HighlightSolutions(selectedSolutions);
                }
            }
        }
        #endregion
    }
}